﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Bes.Swsp.ToolBox
{
  /// <summary>
  /// A collection of network elements that can be looked up by reference or by id.
  /// </summary>
  /// <typeparam name="T">The type of network elements in this collection.</typeparam>
  public class ElementCollection<T> : ICollection<T> where T : Element
  {
    private Dictionary<int, T> _elements = new Dictionary<int, T>();
    private bool _readOnly = false;

    public event EventHandler<ElementEventArgs<T>> ElementAdded;
    public event EventHandler<ElementEventArgs<T>> ElementRemoved;

    /// <summary>
    /// Adds the network element to the collection.
    /// </summary>
    /// <param name="element">The network element to add.</param>
    public void Add(T element)
    {
      if (element == null)
        throw new ArgumentException("Cannot add null element to collection.");

      if (!_readOnly)
      {
        _elements.Add(element.ID, element);
        OnElementAdded(new ElementEventArgs<T>(element));
      }
      else
      {
        throw new InvalidOperationException("The network element collection is read only.");
      }
    }

    /// <summary>
    /// Removes the network element from the collection.
    /// </summary>
    /// <param name="element">The network element to remove.</param>
    public bool Remove(T element)
    {
      if (!_readOnly)
      {
        if (element != null && _elements.Remove(element.ID))
        {
          OnElementRemoved(new ElementEventArgs<T>(element));
          return true;
        }
        else
        {
          return false;
        }
      }
      else
      {
        throw new InvalidOperationException("The network element collection is read only.");
      }
    }

    /// <summary>
    /// Determines whether the collection contains the given network element.
    /// </summary>
    /// <param name="element">The network element to test.</param>
    /// <returns>True if the collection contains the network element, otherwise false.</returns>
    public bool Contains(T element)
    {
      if (element != null)
        return _elements.ContainsKey(element.ID);
      else
        return false;
    }

    /// <summary>
    /// Determines whether the collection contains a network element with the given ID.
    /// </summary>
    /// <param name="id">The ID to test.</param>
    /// <returns>True if the collection contains a network element with the given ID, otherwise, false.</returns>
    public bool Contains(int id)
    {
      return _elements.ContainsKey(id);
    }

    /// <summary>
    /// Empties the collection of all network elements.
    /// </summary>
    public void Clear()
    {
      if (!_readOnly)
      {
        List<T> removedList = _elements.Values.ToList();
        _elements.Clear();
        foreach (T element in removedList)
        {
          OnElementRemoved(new ElementEventArgs<T>(element));
        }
      }
      else
      {
        throw new InvalidOperationException("The network element collection is read only.");
      }
    }

    /// <summary>
    /// Gets the number of elements in this collection.
    /// </summary>
    public int Count
    {
      get
      {
        return _elements.Count;
      }
    }

    /// <summary>
    /// Gets the network element with the given ID.
    /// </summary>
    /// <param name="id">The id of the network element to return.</param>
    /// <returns>The network element in the collection with the given ID.</returns>
    public T this[int id]
    {
      get
      {
        return _elements[id];
      }
    }

    /// <summary>
    /// The IDs of all the network elements in this collection
    /// </summary>
    public IList<int> IdList
    {
      get
      {
        return _elements.Keys.ToArray();
      }
    }

    #region IEnumerable<T> Members

    public IEnumerator<T> GetEnumerator()
    {
      return _elements.Values.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
      return this.GetEnumerator();
    }

    #endregion

    #region ICollection<T> Members


    public void CopyTo(T[] array, int arrayIndex)
    {
      int i = arrayIndex;
      foreach (T element in _elements.Values)
      {
        array[i++] = element;
      }
    }

    public bool IsReadOnly
    {
      get { return _readOnly; }
    }

    #endregion

    public override string ToString()
    {
      return string.Format("Element Count = {0}", this.Count);
    }

    internal void SetReadOnly(bool readOnly)
    {
      _readOnly = readOnly;
    }

    internal protected void OnElementAdded(ElementEventArgs<T> eventArgs)
    {
      if (this.ElementAdded != null)
      {
        this.ElementAdded(this, eventArgs);
      }
    }

    internal protected void OnElementRemoved(ElementEventArgs<T> eventArgs)
    {
      if (this.ElementRemoved != null)
      {
        this.ElementRemoved(this, eventArgs);
      }
    }
  }
}
